package app 
{
	import alternativa.engine3d.containers.ConflictContainer;
	import alternativa.engine3d.controllers.SimpleObjectController;
	import alternativa.engine3d.core.Camera3D;
	import alternativa.engine3d.core.MouseEvent3D;
	import alternativa.engine3d.core.Object3D;
	import alternativa.engine3d.core.Object3DContainer;
	import alternativa.engine3d.core.RayIntersectionData;
	import alternativa.engine3d.core.Sorting;
	import alternativa.engine3d.core.Vertex;
	import alternativa.engine3d.core.View;
	import alternativa.engine3d.materials.FillMaterial;
	import alternativa.engine3d.materials.TextureMaterial;
	import alternativa.engine3d.objects.Mesh;
	import alternativa.engine3d.primitives.Box;
	import alternativa.engine3d.primitives.Plane;
	import debug.DebugView;
	import drawing.ColorPalette;
	import drawing.FurniturePaletteProvider;
	import drawing.IPalette;
	import events.Scene3DEvent;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.filters.GlowFilter;
	import flash.geom.ColorTransform;
	import flash.geom.Matrix;
	import flash.geom.Matrix3D;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.geom.Vector3D;
	import flash.ui.Keyboard;
	import flash.utils.Dictionary;
	import furnitures.FurnitureAlter;
	import furnitures.FurnitureMesh;
	import furnitures.FurnitureModel;
	import resources.Images;
	import utils.CustomContextMenu;
	import utils.DebugWindow;
	import utils.Logger;
	import utils.MathUtil;
	/**
	 * ...
	 * @author Dmitriy Gaiduk
	 */
	public class Scene3DAlter extends Sprite
	{
		private var containerObj:Object3DContainer;
		private var roomContainer:Object3DContainer;
		private var camera:Camera3D;
	

		private var controller:SimpleObjectController;

		private var boxReady:Boolean = false;

		//private var _currentDrag:FurnitureMesh = null;
		private var _currentDrag:FurnitureAlter = null;
		private var _sceneDrag:Object3DContainer = null;
		
		//центр луча
		private var localOrigin:Vector3D = new Vector3D();
		//направление
		private var localDirection:Vector3D = new Vector3D();
		
		private var offsetPoint:Vector3D = new Vector3D();
		
		//private var _furnituries:Vector.<FurnitureMesh>;
		//private var _collisionFurns:Vector.<FurnitureMesh>;
		
		private var _furnituries:Vector.<FurnitureAlter>
		private var _collisionFurns:Vector.<FurnitureAlter>;
		
		private var furnitureMove:FurnitureMesh = null;
		private var furnitureSelected:FurnitureMesh = null;
		
		private var _roomHeight:Number;
		private var _roomWidth:Number;
		private var _roomLenght:Number;
		
		private var _floorPlane:Mesh;
		private var floorWall:Mesh;
		private var rightWall:Mesh;
		private var leftWall:Mesh;
		
		private var _glowFilter:GlowFilter = new GlowFilter();
		
		public var _scaleFactor:Number = AppSettings.SCALE_FACTOR;
		
		private var _sceneWidth:Number;
		private var _sceneHeight:Number;
		
		private const FILTER_WRONG:GlowFilter = new GlowFilter(0xDF0000);
		private const FILTER_SELECTED:GlowFilter = new GlowFilter(0x0080FF);
		
		protected var _isInvalidation:Boolean = false;
		
		private var _enterframeRender:Boolean = false;
		private var _furnitureSelected:FurnitureAlter;
		
		private var _debugView:DebugView;
		private var _tileSize:Number;
		private var _zoom:Number = 0;
		private var containerMain:ConflictContainer;
		private var _touchPlane:Plane;
		
		
		private var _persvictiveRotations: Dictionary;
		private var _currentPerspective:String = "";
		
		
		public static const PERSPECTIVE_LEFT:String = "PERSPECTIVE_LEFT";
		public static const PERSPECTIVE_RIGHT:String = "PERSPECTIVE_RIGHT";
		public static const PERSPECTIVE_TOP:String = "PERSPECTIVE_TOP";
		public static const PERSPECTIVE_CENTR:String = "PERSPECTIVE_CENTR";
		
		
		public static const LEFT_WALL:String = "LEFT_WALL";
		public static const RIGHT_WALL:String = "RIGHT_WALL";
		public static const BOTTOM_WALL:String = "BOTTOM_WALL";
		public static const ROOM:String = "ROOM";
		
		public static const MARK_WRONG:String = "MARK_WRONG";
		public static const MARK_SELECTED:String = "MARK_SELECTED";
		//public static const MARK_MOVED:String = "MARK_MOVED";
		public static const UNMARKED:String = "UNMARKED";
		private var _paletteProvider:FurniturePaletteProvider;
		
		private const PALETTE_SELELECTED:ColorPalette = new ColorPalette(0x0080FF, 0.5);
		private const PALETTE_MOVE:ColorPalette = new ColorPalette(0x00FF00, 0.5);
		private const PALETTE_WRONG:ColorPalette = new ColorPalette(0xDF0000, 0.5);
		
		public function Scene3DAlter(sceneWidth:Number, sceneHeight:Number, 
										paletteProvider:FurniturePaletteProvider) 
		{
			_paletteProvider = paletteProvider;
			
			_sceneWidth = sceneWidth;
			_sceneHeight = sceneHeight;
			_tileSize = 500 * AppSettings.SCALE_FACTOR;
			
			addEventListener(Event.ADDED_TO_STAGE, handleAddedToStage);
		}
		
		
		private function handleAddedToStage(e:Event):void 
		{
			removeEventListener(Event.ADDED_TO_STAGE, handleAddedToStage);
			
			
			
			
			// init perpectives
			_persvictiveRotations = new Dictionary();
			var rotation:Vector3D;
			
			// left
			rotation = new Vector3D( -2.78, Math.PI * 0.5, 1.94);
			_persvictiveRotations[PERSPECTIVE_LEFT] = rotation;
			
			// right
			rotation = new Vector3D(-1.59, 0.0,  -3.14);
			_persvictiveRotations[PERSPECTIVE_RIGHT] = rotation;
			
			// top
			rotation = new Vector3D(-3.1, 0.0, -3.14);
			_persvictiveRotations[PERSPECTIVE_TOP] = rotation;
			
			// centr
			rotation = new Vector3D(-2.36, 0.52, 2.65);
			_persvictiveRotations[PERSPECTIVE_CENTR] = rotation;
			
			
			//init funr arr
			_furnituries = new Vector.<FurnitureAlter>();
			
			// main container
			containerMain = new ConflictContainer();
			
			  
			/*
			 *  Camera x: 3284.440185546875 y: 3100.57568359375 z: 3162.1748046875
				Camera rotation x: -2.2639431953430176 y: 0 z: 2.3317599296569824
				Camera fov(rad): 1.0471975511965976 degree: 59.99999999999999
				
				Camera x: 3323.243408203125 y: 3332.98095703125 z: 3306.425048828125
				Camera rotation x: -2.2348690032958984 y: 0 z: 2.353560447692871
				Camera fov(rad): 1.0471975511965976 degree: 59.99999999999999
			 * */
			
			camera = new Camera3D();
			camera.z =  -5700;
			//camera.x = 3300;
			//camera.y = 3300;
			camera.fov = 1.0471975511965976;
			
			containerMain.addChild(camera);
			
			
			
			//controller.lookAtXYZ(0, 0, 0);
			
			
			//camera.view = new View(stage.stageWidth, stage.stageHeight);
			camera.view = new View(_sceneWidth, _sceneHeight);
			addChild(camera.view);
			//addChild(camera.diagram);
			camera.view.hideLogo();
			CustomContextMenu.getInstance().createMyMenu(camera.view, DebugWindow, this);
			/*
			 * rotation x: -2.154608726501465 y: 0.7853981256484985 z: 2.751511573791504
				rotation degreee x: -123.44998653059102 y: 44.9999978371445 z: 157.65000045965215
				
				rotation x: -2.363175630569458 y: 0.5235987901687622 z: 2.6450464725494385
				rotation degreee x: -135.39998987979695 y: 30.000000834826057 z: 151.54999949304877
				.rotationY = 30 * Math.PI / 180;;
				// left
				rotation x: -1.5952308177947998 y: 0 z: -3.135483980178833
				rotation degreee x: -91.39999320884485 y: 0 z: -179.6499987951282
				
				// rigth
				rotation x: -2.778563976287842 y: 1.5184382200241089 z: 1.9425514936447144
				rotation degreee x: -159.19998894838147 y: 87.00010145873853 z: 111.30000207267629
				
				// up
				rotation x: -3.104067802429199 y: 0 z: -3.135483980178833
				rotation degreee x: -177.8499844016414 y: 0 z: -179.6499987951282
				
				pos
				76.98935861441691 y: -402.14954053509973 z: 0

			 * */
			containerObj = new  Object3DContainer();
			roomContainer = new Object3DContainer();
			containerObj.addChild(roomContainer);
			
			//containerObj.rotationX =   -2.363175630569458;
			//containerObj.rotationY = 30 * Math.PI / 180;;
			//containerObj.rotationZ = 2.6450464725494385;
			
			containerObj.x =  77;
			containerObj.y =  -402;
			containerObj.z = 0;
			
			containerObj.rotationX =  -2.363175630569458;
			containerObj.rotationY = 0;
			containerObj.rotationZ = 2.6450464725494385;
			
			containerMain.addChild(containerObj);
			controller = new SimpleObjectController(stage, camera, 400);
			controller.disable();
			makeFrontTouch();
			
			//makeAxis();
			
			makeField();
			
			updateWalls();
			
			addEventListener(Event.ENTER_FRAME, handleEnterFrame);
		
			stage.addEventListener(MouseEvent.MOUSE_UP, handleStageMouseUp);
			stage.addEventListener(MouseEvent.MOUSE_MOVE, handleStageMouseMove);
			
			// test
			stage.addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
			stage.addEventListener(KeyboardEvent.KEY_DOWN, handleKeyDown);
			stage.addEventListener(KeyboardEvent.KEY_UP, handleKeyUp);
			invalidate();
			
			
			
			// debug
			/*var shape:Shape = new Shape();
			addChild(shape);
			
			shape.graphics.lineStyle(2, 0x80FF80);
			shape.graphics.drawRect(0, 0, _sceneWidth, _sceneHeight);
			
			shape.graphics.lineStyle(0, 0xFF0000);
			shape.graphics.drawRect(0, 0, 100, 100);
			shape.graphics.endFill();
			_debugView = new DebugView(shape.graphics);
			*/
		}
		
		private function makeFrontTouch():void 
		{
			var fillMat:FillMaterial = new FillMaterial(0xFF8000, 1);
			
			_touchPlane = new Plane(9000, 5000);
			//_touchPlane.setMaterialToAllFaces(fillMat);
			//plane.rotationX = 90 * Math.PI / 180;
			containerMain.addChild(_touchPlane);
		}
		
		private function handleKeyUp(e:KeyboardEvent):void 
		{
			_enterframeRender = false;
		}
		
		private function handleKeyDown(e:KeyboardEvent):void 
		{
			
			
			switch (e.keyCode) {
				case Keyboard.SPACE:
						log("================================================");
						log("Current obj:" + controller.object);
						log("position x: " + controller.object.x + " y: " + controller.object.y + " z: " + controller.object.z);
						log("rotation x: " + controller.object.rotationX + " y: " + controller.object.rotationY + 
							" z: " + controller.object.rotationZ);
						log("rotation degreee x: " + toDegree(controller.object.rotationX) + 
								" y: " + toDegree(controller.object.rotationY) + 
							" z: " + toDegree(controller.object.rotationZ));
						var rotX:Number
						var fovDegree:Number = camera.fov * 180 / Math.PI;
						log("Camera fov(rad): " + camera.fov + " degree: " + fovDegree);
						
					break;
				case Keyboard.UP:
						camera.fov += 10 * Math.PI / 180;
					break;
				case Keyboard.DOWN:
						camera.fov -= 10 * Math.PI / 180;
					break;
				case Keyboard.TAB:
					if (controller.object == camera) {
						log("Controller: obj");
						controller.object = containerObj;
					}else {
						log("Controller: camera");
						controller.object = camera;
					}
					break;
			}
			_enterframeRender = true;
			log("_enterframeRender: " + _enterframeRender);
		}
		
		private function toDegree(rad:Number):Number {
			return rad*180 / Math.PI;
		}
		
		private function handleMouseDown(e:MouseEvent):void 
		{
			_enterframeRender = true;
			
		}
		
		private function makeAxis():void 
		{
			var axisW:Number = 20;
			var axisLen:Number = 150;
			
			
			var _axisObj:Object3DContainer = new Object3DContainer();
			
			//var colorMat:FillMaterial = new FillMaterial(0x00FF00, 1, 1, 0x000000);
			var colorMat:FillMaterial = new FillMaterial(0x00FF00, 1);
			var box:Box = new Box(axisLen, axisW, axisW);
			box.x += axisLen * 0.5;
			box.sorting = Sorting.AVERAGE_Z;
			box.setMaterialToAllFaces(colorMat);
			_axisObj.addChild(box);
			
			//colorMat = new FillMaterial(0xff0000, 1, 1, 0x000000);
			colorMat = new FillMaterial(0xff0000, 1 );
			box = new Box(axisW, axisLen, axisW);
			box.y += axisLen * 0.5;
			box.sorting = Sorting.AVERAGE_Z;
			box.setMaterialToAllFaces(colorMat);
			_axisObj.addChild(box);
			
			
			//colorMat = new FillMaterial(0x0000FF, 1, 1, 0x000000);
			colorMat = new FillMaterial(0x0000FF, 1 );
			box = new Box(axisW, axisW, axisLen);
			box.z += axisLen * 0.5;
			box.setMaterialToAllFaces(colorMat);
			box.sorting = Sorting.AVERAGE_Z;
			_axisObj.addChild(box);
			
			
			
			containerObj.addChild(_axisObj);
		}
		
	
		private function handleFurnDown(e:MouseEvent3D):void 
		{
			if (_furnitureSelected != null) {
				markFurniture(_furnitureSelected, UNMARKED);
				_furnitureSelected = null;
			}
			
			_currentDrag = e.currentTarget as FurnitureAlter;
			
			if (_currentDrag == null) {
				return;
			}
			
			markFurniture(_currentDrag, MARK_SELECTED);
			
			// get possible collisions
			_collisionFurns = new Vector.<FurnitureAlter>();
			var top:Number = _currentDrag.z + _currentDrag.furnitureHeight;
			var bottom:Number = _currentDrag.z;
			//trace("===============================================");
			//trace("top: " + top + " bottom: " + bottom);
			
			for (var i:int = 0 ; i < _furnituries.length; i++ ) {
				if (_currentDrag == _furnituries[i]) {
					//trace("continue");
					continue;
				}
				if (MathUtil.intersectSegmetsInLine(top, bottom, _furnituries[i].z, 
					_furnituries[i].z + _furnituries[i].furnitureHeight)) {
					_collisionFurns.push(_furnituries[i]);	
				}
				//if ((top >= _furnituries[i].z && top <= (_furnituries[i].z + _furnituries[i].furnitureHeight)) || 
					//(bottom >= _furnituries[i].z && bottom <= (_furnituries[i].z + _furnituries[i].furnitureHeight)))
			}
			//trace("furnitures: " + _furnituries.length + " collisions: " + _collisionFurns.length);
			
			
			
			var irdFurn:RayIntersectionData = calcPointByMouse(_currentDrag);
			
			if (irdFurn != null) {
				var glob:Vector3D = irdFurn.object.localToGlobal(irdFurn.point);
				var localP:Vector3D = containerObj.globalToLocal(glob);
				_floorPlane.z = localP.z;
			}else {
				_floorPlane.z = 0;
			}
			
			var irdPlane:RayIntersectionData = calcPointByMouse(_floorPlane);
			if (irdPlane != null) {
				//trace("ird furn: " + irdPlane);
				offsetPoint.x = _currentDrag.x - irdPlane.point.x;
				offsetPoint.y = _currentDrag.y - irdPlane.point.y;
				offsetPoint.z = _currentDrag.z - irdPlane.point.z;
			}else {
				offsetPoint.x = 0;
				offsetPoint.y = 0;
				offsetPoint.z = 0;
			}
			
			//offsetPoint.x = 0;
			///offsetPoint.y = 0;
			//offsetPoint.z = 0;
			
			invalidate();
		}
		
		private function handleStageMouseMove(e:MouseEvent):void 
		{
			var ird:RayIntersectionData;
				
			if (_sceneDrag != null) {
				ird = calcPointByMouse(_touchPlane);
				if (ird == null) {
					return;
				}
				
				_sceneDrag.x = ird.point.x + offsetPoint.x;
				_sceneDrag.y= ird.point.y + offsetPoint.y;
				_sceneDrag.z= 0;
				return;
			}
			
			if (_currentDrag == null) {
				return;
			}
			
			
			
			ird = calcPointByMouse(_floorPlane);
			if (ird == null) {
				//log("stage move: ird with floorplane is null ");
				return;
			}
		
			//_currentDrag.x = ird.point.x + offsetPoint.x;
			//_currentDrag.y = ird.point.y + offsetPoint.y;
		//	return;
			
			//trace("drag x: " + _currentDrag.x + " y: " + _currentDrag.y + " z: " + _currentDrag.z);
			
			var targetX:Number = ird.point.x + offsetPoint.x;
			var targetY:Number = ird.point.y + offsetPoint.y;
			//_currentDrag.x = ird.point.x + offsetPoint.x;
			//_currentDrag.y = ird.point.y + offsetPoint.y;
			_currentDrag.z = _currentDrag.positionY;
			
			/*if (_currentDrag.rotationZ == 0) {
				//_currentDrag.x = ird.point.x + offsetPoint.x;
				//_currentDrag.y = 0;
				
				if ((targetX- _currentDrag.furnitureWidth) < -_currentDrag.furnitureWidth*0.5) {
					_currentDrag.rotationZ = -90 * Math.PI / 180;
					_currentDrag.x = 0; 
				}else if ((targetX- _currentDrag.furnitureWidth) >0 ){
					_currentDrag.x = targetX;
				}
			}else {
				_currentDrag.x = 0;
				_currentDrag.y = ird.point.y + offsetPoint.y;
				
				if (_currentDrag.y < 0) {
					_currentDrag.rotationZ = 0;
					_currentDrag.x = _currentDrag.furnitureWidth;
				}
			}*/
			//trace("===================================================");
			//trace("put to axisX targetx " + targetX  + " targetY: " + targetY);
			var ghostX:Number = _currentDrag.x; 
			var ghostY:Number = _currentDrag.y; 
			var ghostRotationZ:Number = _currentDrag.rotationZ; 
			
			
			
			//if ((targetY - _currentDrag.furnitureWidth*0.5) < targetX) {
			if ((targetY) < targetX) {
				// put to axisX
				//trace("put to axisX");
				ghostRotationZ = 0;
				ghostX =  targetX < 0 ? 0 : targetX;
				ghostY = 0;
				
			}else if(targetY > targetX){
				// put to axisY
				//trace("put to axisY");
				ghostRotationZ = -90 * Math.PI / 180;
				ghostX = 0; 
				ghostY = (targetY - _currentDrag.furnitureWidth) < 0 ? 
									_currentDrag.furnitureWidth : targetY;
			}else {
				if (_currentDrag.rotationZ == 0) {
					//trace("move X");
					ghostX =  targetX < 0 ? 0 : targetX;
				}else {
					//trace("move Y");
					ghostY = (targetY - _currentDrag.furnitureWidth) < 0 ? 
									_currentDrag.furnitureWidth : targetY;
				}
			}
			
			// check room
			if(ghostRotationZ == 0){
				if ((ghostX + _currentDrag.furnitureWidth) > roomLenght)  {
						ghostX = roomLenght - _currentDrag.furnitureWidth;
					}
			}else {
				
				if (ghostY > roomWidth) {
					ghostY = roomWidth;
				}
			}
			
			// check intersect
			var pointLeftUpper:Point = new Point(0,0);
			var pointRightDown:Point = new Point(0,0);
			if (ghostRotationZ == 0) {
					pointLeftUpper.x = ghostX;
					pointLeftUpper.y = ghostY;
					pointRightDown.x = ghostX + _currentDrag.furnitureWidth;
					pointRightDown.y = ghostY + _currentDrag.furnitureDepth;
				}else {
					pointLeftUpper.x = ghostX;
					pointLeftUpper.y = ghostY -  _currentDrag.furnitureWidth;
					pointRightDown.x = ghostX + _currentDrag.furnitureDepth;
					pointRightDown.y = ghostY;
				}
			
			/*trace("--------");
			trace("pointLeftUpper up x: " + pointLeftUpper.x + " y: " +pointLeftUpper.y);
			trace("pointRightDown down x: " + pointRightDown.x + " y: " +pointRightDown.y);*/
			var pointCheckLU:Point = new Point();
			var pointCheckRD:Point = new Point();
			
			var isIntersect:Boolean = false; 
			// unmark
			for (var i:int = 0 ; i < _furnituries.length; i++ ) {
				if (_furnituries[i] == _currentDrag) {
					continue;
				}
				markFurniture(_furnituries[i], UNMARKED);
			}
			
			for (i = 0 ; i < _collisionFurns.length; i++ ) {
				
				if (_collisionFurns[i].rotationZ == 0) {
					//trace("rotation 0");
					pointCheckLU.x = _collisionFurns[i].x;
					pointCheckLU.y = _collisionFurns[i].y;
					pointCheckRD.x = _collisionFurns[i].x + _collisionFurns[i].furnitureWidth;
					pointCheckRD.y = _collisionFurns[i].y + _collisionFurns[i].furnitureDepth;
				}else {
					pointCheckLU.x = _collisionFurns[i].x;
					pointCheckLU.y = _collisionFurns[i].y -  _collisionFurns[i].furnitureWidth;
					pointCheckRD.x = _collisionFurns[i].x + _collisionFurns[i].furnitureDepth;
					pointCheckRD.y = _collisionFurns[i].y; 
				}
				
				/*trace("--------");
				trace("up x: " + pointCheckLU.x + " y: " +pointCheckLU.y);
				trace("down x: " + pointCheckRD.x + " y: " +pointCheckRD.y);*/
				if (MathUtil.intersectBoundingBox(pointLeftUpper, pointRightDown, pointCheckLU, pointCheckRD))
				{
				
					var goodPos:Point = getGoodPos(ghostX, ghostY, ghostRotationZ, _collisionFurns[i]);
					if (goodPos != null) {
						ghostX = goodPos.x;
						ghostY = goodPos.y;
						isIntersect = false;
					}else{
						markFurniture(_collisionFurns[i], MARK_WRONG);
						isIntersect = true;
					}
					break;
				}
			}
			
			if(!isIntersect){
				_currentDrag.x = ghostX;
				_currentDrag.y = ghostY;
				_currentDrag.rotationZ = ghostRotationZ;
			}
			
			invalidate();
		}
		
		/**
		 * If good position, return true
		 * @param	furnArr
		 * @param	checkWith
		 * @param	exclude
		 * @return
		 */
		private function getGoodPos(checkGhostX:Number, checGhostY:Number, 
									ghostRotationZ:Number,
									nearFurn:FurnitureAlter):Point {
		var ghostX:Number = checkGhostX;								
		var ghostY:Number = checGhostY;	
		
		if (ghostRotationZ == 0) {
			
			if (nearFurn.rotationZ != 0) {
				// corner
				ghostX = nearFurn.furnitureDepth + 1;
			}else {
				// inline, x axis
				var centrX:Number = nearFurn.x + nearFurn.furnitureWidth * 0.5;
				// check put left
				if (Math.abs(centrX - ghostX - _currentDrag.furnitureWidth) > Math.abs(centrX - ghostX)) {
					ghostX = nearFurn.x + nearFurn.furnitureWidth + 1;
				}else {
					ghostX = nearFurn.x - _currentDrag.furnitureWidth -1;
				}
			}
			
			// check room
			if (ghostX < 0) {
				return null;
			}else if ((ghostX + _currentDrag.furnitureWidth) > roomLenght) {
				return null;
			}
			
		}else {
			if (nearFurn.rotationZ == 0) {
				// corner
				ghostY = nearFurn.furnitureDepth + _currentDrag.furnitureWidth + 1;
			}else {
				// inline, y axis
				
				var centrY:Number = nearFurn.y - nearFurn.furnitureWidth * 0.5;
				
				// check put left
				var leftPt:Number = Math.abs(centrY - (ghostY - _currentDrag.furnitureWidth));
				var rightPt:Number = Math.abs(centrY - ghostY);
				
				if (leftPt > rightPt) {
					ghostY = nearFurn.y - nearFurn.furnitureWidth - 1;
				}else {
					ghostY = nearFurn.y + _currentDrag.furnitureWidth + 1;
				}
			}
			
			// check room
			if ((ghostY - _currentDrag.furnitureWidth) < 0) {
				return null;
			}else if (ghostY > roomWidth) {
				return null;
			}
		}
		
		var pointLeftUpper:Point = new Point(0,0);
		var pointRightDown:Point = new Point(0, 0);
		
		if (ghostRotationZ == 0) {
				pointLeftUpper.x = ghostX;
				pointLeftUpper.y = ghostY;
				pointRightDown.x = ghostX + _currentDrag.furnitureWidth;
				pointRightDown.y = ghostY + _currentDrag.furnitureDepth;
			}else {
				pointLeftUpper.x = ghostX;
				pointLeftUpper.y = ghostY -  _currentDrag.furnitureWidth;
				pointRightDown.x = ghostX + _currentDrag.furnitureDepth;
				pointRightDown.y = ghostY;
			}
			
			var pointCheckLU:Point = new Point();
			var pointCheckRD:Point = new Point();
			
			var isIntersect:Boolean = false; 
			
			var count:int = _collisionFurns.length;
			
			for (var i:int = 0 ; i < count; i++ ) {
				if (_collisionFurns[i] == nearFurn) {
					continue;
				}
				if (_collisionFurns[i].rotationZ == 0) {
					//trace("rotation 0");
					pointCheckLU.x = _collisionFurns[i].x;
					pointCheckLU.y = _collisionFurns[i].y;
					pointCheckRD.x = _collisionFurns[i].x + _collisionFurns[i].furnitureWidth;
					pointCheckRD.y = _collisionFurns[i].y + _collisionFurns[i].furnitureDepth;
				}else {
					pointCheckLU.x = _collisionFurns[i].x;
					pointCheckLU.y = _collisionFurns[i].y -  _collisionFurns[i].furnitureWidth;
					pointCheckRD.x = _collisionFurns[i].x + _collisionFurns[i].furnitureDepth;
					pointCheckRD.y = _collisionFurns[i].y; 
				}
				
				/*trace("--------");
				trace("up x: " + pointCheckLU.x + " y: " +pointCheckLU.y);
				trace("down x: " + pointCheckRD.x + " y: " +pointCheckRD.y);*/
				if (MathUtil.intersectBoundingBox(pointLeftUpper, pointRightDown, pointCheckLU, pointCheckRD))
				{
					//markFurniture(_collisionFurns[i], MARK_WRONG);
					
					isIntersect = true;
					return null;
					break;
				}
			}
			
			return new Point(ghostX, ghostY);
		}
		
		private function markFurniture(furn:FurnitureAlter, markType:String):void {
			if (furn == null) {
				return;
			}
			var fillMat:FillMaterial;
			
			switch(markType) {
				/*case MARK_MOVED:
					//furn.filters = [FILTER_MOVE];
					furn.setFacadePalette(PALETTE_MOVE, PALETTE_MOVE);
					furn.setCorpusPalette(PALETTE_MOVE);
					furn.setTabletopPalette(PALETTE_MOVE);
					break;*/
				case MARK_SELECTED:
					furn.filters = [FILTER_SELECTED];
					//furn.setFacadePalette(PALETTE_SELELECTED, PALETTE_SELELECTED);
					//furn.setCorpusPalette(PALETTE_SELELECTED);
					//furn.setTabletopPalette(PALETTE_SELELECTED);
					break;
				case MARK_WRONG:
					furn.filters = [FILTER_WRONG];
					//furn.setFacadePalette(PALETTE_WRONG, PALETTE_WRONG);
					//furn.setCorpusPalette(PALETTE_WRONG);
					//furn.setTabletopPalette(PALETTE_WRONG);
					break;
				case UNMARKED:
						furn.filters = [];
						//furn.setFacadePalette(_paletteProvider.facadePalette, _paletteProvider.techPalette);
						//furn.setCorpusPalette(_paletteProvider.corpusPalette);
						//furn.setTabletopPalette(_paletteProvider.tabletopPalette);
					break;
			}
			
		}
		
		
		private function handlePlaneDown(e:MouseEvent3D):void 
		{
			if (_furnitureSelected != null) {
				markFurniture(_furnitureSelected, UNMARKED);
			}
			
			_furnitureSelected = null;
			_sceneDrag = containerObj;
			
			var irdPlane:RayIntersectionData = calcPointByMouse(_touchPlane);
			
			if (irdPlane != null) {
				//trace("ird furn: " + irdPlane);
				offsetPoint.x = _sceneDrag.x - irdPlane.point.x;
				offsetPoint.y = _sceneDrag.y - irdPlane.point.y;
				offsetPoint.z = _sceneDrag.z - irdPlane.point.z;
			}else {
				offsetPoint.x = 0;
				offsetPoint.y = 0;
				offsetPoint.z = 0;
			}
			
			
			//camera.view.startDrag();
		}
		
		private function handleStageMouseUp(e:MouseEvent):void 
		{
			
			if (_currentDrag != null)
			{
				for (var i:int = 0; i < _furnituries.length; i++ ) {
					if (_furnituries[i] != _currentDrag) {
						markFurniture(_furnituries[i], UNMARKED);
					}
				}
			
				markFurniture(_currentDrag, MARK_SELECTED);
				_furnitureSelected = _currentDrag;
				_currentDrag = null;
			}
			
			_currentDrag = null;
			_sceneDrag = null;
			
			
			
			// test
			_enterframeRender = false;
			invalidate();
		}
		
	/*	public function setRotationScene(angle:Vector3D):void
		{
			containerObj.rotationX = angle.x;
			containerObj.rotationY = angle.y;
			containerObj.rotationZ = angle.z;
			invalidate();
		}
		*/
		
		public function setPerspective(perspective:String):void {
			var rotation:Vector3D = _persvictiveRotations[perspective] as Vector3D;
			
			if (rotation == null) {
				log("rotation not found: " + perspective);
				return;
			}
			_currentPerspective = perspective;
			containerObj.rotationX = rotation.x;
			containerObj.rotationY = rotation.y;
			containerObj.rotationZ = rotation.z;
			invalidate();
		}
		
		public function removeSelected():void {
			if (_furnitureSelected != null)
			{
				var index:int = _furnituries.indexOf(_furnitureSelected);
				if (index > -1)
				{
					_furnituries.splice(index, 1);
				}else {
					log("Furn for delete not found in array");
				}
				if (_furnitureSelected.parent != null)
				{
					_furnitureSelected.parent.removeChild(_furnitureSelected);
				}
				_furnitureSelected = null;
			}
			invalidate();
		}
		
		public function getSizesNeeded():Vector.<String>
		{
			var sizesNeeded:Vector.<String> = new Vector.<String>();
			var sizeForFurniture:Vector.<String>;
			for (var i:int = 0 ; i < _furnituries.length; i++ ) {
				
				sizeForFurniture =_furnituries[i].getSizesTexture()
				for each(var sizetype:String in sizeForFurniture)
				{
					sizesNeeded.push(sizetype);
				}
			}
			
			return sizesNeeded;
		}
		
		public function addFurnitureWithPalette(furnModel:FurnitureModel, 
												facade:IPalette, 
												tabletop:IPalette, 
												corpus:IPalette,
												tech:IPalette):void {
										
			//var furn:FurnitureMesh = new FurnitureMesh(furnModel, SCALE_FACTOR, _debugView);
			log("scale factor: " + _scaleFactor);
			var furn:FurnitureAlter = new FurnitureAlter(furnModel, _scaleFactor, null);
			// add to array
			_furnituries.push(furn);
			
			// init
			furn.addEventListener(MouseEvent3D.MOUSE_DOWN, handleFurnDown);
			furn.z = furn.positionY;
			furn.x = _roomLenght * 0.5;
			furn.y = _roomWidth * 0.5;
			
			// set palette
			furn.setFacadePalette(facade, tech);
			furn.setTabletopPalette(tabletop);
			furn.setCorpusPalette(corpus);
			
			roomContainer.addChild(furn);
			invalidate();
			
			
		}
		
		private function makeField():void 
		{
			_floorPlane = new Plane(5000, 5000);
			_floorPlane.x =0;
			_floorPlane.y =0;
			var colorFill:FillMaterial = new FillMaterial(0x008040);
			//_floorPlane.setMaterialToAllFaces(colorFill);
			//_floorPlane.sorting = Sorting.DYNAMIC_BSP;
			
			//_floorPlane.addEventListener(MouseEvent3D.MOUSE_MOVE, handlePlaneMove);
			
			containerObj.addChild(_floorPlane);
			
		}
		
		private function handlePlaneMove(e:MouseEvent3D):void 
		{
			log("move");
			var data:RayIntersectionData = Object3D(e.target).intersectRay(e.localOrigin, e.localDirection);
			
		
			trace("=========================================");
			trace("mouse move. local orign x: " + e.localOrigin.x + 
				" y: " + e.localOrigin.y + " z: " + e.localOrigin.z);
				trace("mouse move. local localDirection x: " + e.localDirection.x + 
				" y: " + e.localDirection.y + " z: " + e.localDirection.z);
				
			if (_currentDrag == null || data == null) {
				return;
			}
			
			
			var point:Vector3D = data.point;
			_currentDrag.x = point.x;
			_currentDrag.y = point.y;
			_currentDrag.z = point.z;
			
			invalidate();
		}
		
		private function calcPointByMouse(hitObj:Object3D):RayIntersectionData {
				
			return calcPoint(hitObj,  camera.view.mouseX, camera.view.mouseY);
		}
		
		private function calcPoint(hitObj:Object3D, screenX:Number, screenY:Number):RayIntersectionData {
			
			var pOrigin:Vector3D = new Vector3D(0,0, 0);
			
			var dDirection:Vector3D = new Vector3D(0,0,0);
			// This calculate ray from camera to mouse cursor.
			
			//camera.calculateRay(p, d, stage.mouseX, stage.mouseY);
			camera.calculateRay(pOrigin, dDirection, screenX, screenY);
			
			// _containerSurface - объект, с которм ищем пересечения

			var concatenatedMatrix:Matrix3D = hitObj.concatenatedMatrix.clone();

			concatenatedMatrix.invert();
			
			pOrigin = concatenatedMatrix.transformVector(pOrigin);
			
			dDirection = concatenatedMatrix.deltaTransformVector(dDirection);
				
			return hitObj.intersectRay(pOrigin, dDirection, null, camera);
		}
		
		
		public function updateWalls():void {
			
			// clear listeners
			if (rightWall != null)
			{
				rightWall.removeEventListener(MouseEvent.MOUSE_DOWN, handlePlaneDown);
			}
			if (leftWall != null)
			{
				leftWall.removeEventListener(MouseEvent.MOUSE_DOWN, handlePlaneDown);
			}
			if (floorWall != null)
			{
				floorWall.removeEventListener(MouseEvent3D.MOUSE_DOWN, handlePlaneDown);
			}
			
			safeRemoveObject(leftWall);
			safeRemoveObject(rightWall);
			safeRemoveObject(floorWall);
			
			// размер плитки
			
			var textureSize:Number = _tileSize; // 500 millimeters
			/*var shapeTexture:Shape = new Shape();
			var gr:Graphics = shapeTexture.graphics;
			gr.lineStyle(2, 0x000000);
			gr.beginFill(0xFFFFCC, 1);
			gr.drawRect(0, 0, textureSize, textureSize);
			gr.endFill();*/
			
			var shapeTexture:DisplayObject = Images.getTile();
			shapeTexture.width = _tileSize;
			shapeTexture.height = _tileSize;
			var texture:BitmapData = new BitmapData(shapeTexture.width, shapeTexture.height, true, 0xFFFFCC);
			texture.draw(shapeTexture);
			
			//prevent z-fighting
			// some space between walls and furnituries
			var wallSpacing:Number = 25 * _scaleFactor;
			if (wallSpacing < 5) {
				wallSpacing = 5;
			}
			// floor
			floorWall = createPlane(_roomLenght, _roomWidth, texture);
			floorWall.sorting = Sorting.DYNAMIC_BSP;
			floorWall.x = roomLenght * 0.5 -wallSpacing;
			floorWall.y = roomWidth * 0.5 -wallSpacing;
			
			
			//leftWall
			leftWall = createPlane(_roomLenght, _roomHeight, texture);
			leftWall.sorting = Sorting.DYNAMIC_BSP;
			leftWall.rotationX = 90 * Math.PI / 180;
			leftWall.rotationY = 180 * Math.PI / 180;
			leftWall.z = _roomHeight * 0.5;
			leftWall.x = _roomLenght * 0.5 -wallSpacing;
			leftWall.y =-wallSpacing;
			
			//rigth
			rightWall = createPlane(_roomWidth, _roomHeight, texture);
			rightWall.sorting = Sorting.DYNAMIC_BSP;
			rightWall.rotationX = 90 * Math.PI / 180;
			rightWall.rotationZ = 90 * Math.PI / 180;
			rightWall.rotationY = 180 * Math.PI / 180;
			rightWall.z = _roomHeight * 0.5;
			rightWall.y = _roomWidth * 0.5 -wallSpacing;
			rightWall.x = -wallSpacing;
			
			floorWall.addEventListener(MouseEvent3D.MOUSE_DOWN, handlePlaneDown);
			leftWall.addEventListener(MouseEvent3D.MOUSE_DOWN, handlePlaneDown);
			rightWall.addEventListener(MouseEvent3D.MOUSE_DOWN, handlePlaneDown);
			
			roomContainer.addChild(floorWall);
			roomContainer.addChild(leftWall);
			roomContainer.addChild(rightWall);
		}
		
		private function createPlane(planeW:Number, planeH:Number, texture:BitmapData):Mesh
		{
			/*var vertices:Vector.<Vertex> = new Vector.<Vertex>();
			var vert:Vertex;
			
			var mesh:Mesh = new Mesh();
			vert = mesh.addVertex(0, 0, 0, 0, 0);
			vertices.push(vert);
			
			vert = mesh.addVertex(planeW, 0, 0, 1, 0);
			vertices.push(vert);
			
			vert = mesh.addVertex(planeW, planeH, 0, 1, 1);
			vertices.push(vert);
			
			vert = mesh.addVertex(0, planeH, 0, 0, 1);
			vertices.push(vert);
			
			vert = mesh.addVertex(0, 0, 0, 0, 0);
			vertices.push(vert);
			
			mesh.addFace(vertices);
			mesh.calculateFacesNormals();
			
			*/
			var shape:Shape = new Shape();
			shape.graphics.beginBitmapFill(texture, null, true, true);
			shape.graphics.drawRect(0 , 0, planeW, planeH);
			shape.graphics.endFill();
			
			var mesh:Mesh = new Plane(planeW, planeH, 10, 10);
			var bmData:BitmapData = new BitmapData(shape.width, shape.height, true, 0xFF0000);
			bmData.draw(shape);
			
			
			var colorMat:FillMaterial = new FillMaterial();
			var textureMat:TextureMaterial = new TextureMaterial(bmData, false, true, 0 , 1);
			textureMat.repeat = true;
			
			mesh.setMaterialToAllFaces(textureMat);
			mesh.sorting = Sorting.DYNAMIC_BSP;
			
			return mesh;
			
		}
		
		private function safeRemoveObject(obj:Object3D):void
		{
			
			if (obj != null && obj.parent != null)
			{
				obj.parent.removeChild(obj);
			}
		}
		
		public function setRoomSize(w:Number, l:Number, h:Number):void{
			_roomWidth = w;
			_roomHeight = h;
			_roomLenght = l;
			updateWalls();
			
			invalidate();
		}
		
		public function setFacadePalette(palette:IPalette, techPalette:IPalette):void {
			
			for (var i:int = 0 ; i < _furnituries.length; i++ ) {
				_furnituries[i].setFacadePalette(palette, techPalette);
			}
			
			invalidate();
		}
		
		public function setTabletopPalette(palette:IPalette):void {
			
			for (var i:int = 0 ; i < _furnituries.length; i++ ) {
				_furnituries[i].setTabletopPalette(palette);
			}
			
			invalidate();
			
		}
		public function setCorpusPalette(palette:IPalette):void {
			for (var i:int = 0 ; i < _furnituries.length; i++ ) {
				_furnituries[i].setCorpusPalette(palette);
			}
			
			invalidate();
			
		}
		
		public function get zoom():Number {
			return _zoom;
		}
		
		public function set zoom(value:Number):void {
			_zoom = value;
		//	camera.x = 3300 - value * 500;
			//camera.y = 3300 - value * 500;
			//camera.z = 3300 - value * 500;
			camera.z = -5700 + value * 1000;
			
			invalidate();
		}
		
		public function get roomHeight():Number 
		{
			return _roomHeight;
		}
		
		public function set roomHeight(value:Number):void 
		{
			_roomHeight = value;
		}
		
		public function get roomWidth():Number 
		{
			return _roomWidth;
		}
		
		public function set roomWidth(value:Number):void 
		{
			_roomWidth = value;
		}
		
		public function get roomLenght():Number 
		{
			return _roomLenght;
		}
		
		public function set roomLenght(value:Number):void 
		{
			_roomLenght = value;
		}
		
		public function get currentPerspective():String 
		{
			return _currentPerspective;
		}
		
		
		private function handleEnterFrame(e:Event):void 
		{
			if (_currentDrag == null) {
				controller.accelerate(true);
				
				controller.update();
				
			}	
			if (_enterframeRender) {
					camera.render();
				}
		}
		
		public function invalidate():void
		{
			if (!_isInvalidation)
			{
				_isInvalidation = true;
				addEventListener(Event.ENTER_FRAME, onInvalidate);
			}
		}
		
		public function getBoundsRect(elementName:String):Rectangle {
			var boundRect:Rectangle;
			
			switch(elementName) {
				case ROOM:
					return getScreenRect(roomContainer);
				break;
				case LEFT_WALL:
					return getScreenRect(leftWall);
				break;
				case RIGHT_WALL:
					return getScreenRect(rightWall);
				break;
				case BOTTOM_WALL:
					return getScreenRect(floorWall);
				break;
			}
			
			return null;
		}
		
		
		public function getRoomCentr():Point {
			var projPoint:Vector3D = camera.projectGlobal(containerObj.localToGlobal(new Vector3D(0, 0, 0)));
			return new Point(projPoint.x, projPoint.y);
		}
		
		public function getDebugGr():Graphics {
			return _debugView.getSurface();
		}
		
		public function setRoomScreenPos(posX:Number, posY:Number):void {
			
			var irData:RayIntersectionData = calcPoint(_touchPlane, posX, posY);
			if (irData == null) {
				log("setRoomScreenPos : ir data null");
				return;
			}
			containerObj.x = irData.point.x;
			containerObj.y = irData.point.y;
			invalidate();
		}
		
		private function getScreenRect(obj:Object3D):Rectangle {
			obj.calculateBounds();
			var points:Vector.<Point> = new Vector.<Point>();
			
			
			var p3d:Vector3D;
			var projPoint:Vector3D;
			
			p3d = new Vector3D(obj.boundMinX, obj.boundMinY, obj.boundMinZ);
			projPoint= camera.projectGlobal(obj.localToGlobal(p3d));
			points.push(new Point(projPoint.x, projPoint.y));
			
			p3d = new Vector3D(obj.boundMaxX, obj.boundMinY, obj.boundMinZ);
			projPoint = camera.projectGlobal(obj.localToGlobal(p3d));
			points.push(new Point(projPoint.x, projPoint.y));
			
			p3d = new Vector3D(obj.boundMinX, obj.boundMaxY, obj.boundMinZ);
			projPoint = camera.projectGlobal(obj.localToGlobal(p3d));
			points.push(new Point(projPoint.x, projPoint.y));
			
			p3d = new Vector3D(obj.boundMaxX, obj.boundMaxY, obj.boundMinZ);
			projPoint = camera.projectGlobal(obj.localToGlobal(p3d));
			points.push(new Point(projPoint.x, projPoint.y));
			
			p3d = new Vector3D(obj.boundMinX, obj.boundMinY, obj.boundMaxZ);
			projPoint= camera.projectGlobal(obj.localToGlobal(p3d));
			points.push(new Point(projPoint.x, projPoint.y));
			
			p3d = new Vector3D(obj.boundMaxX, obj.boundMinY, obj.boundMaxZ);
			projPoint = camera.projectGlobal(obj.localToGlobal(p3d));
			points.push(new Point(projPoint.x, projPoint.y));
			
			p3d = new Vector3D(obj.boundMinX, obj.boundMaxY, obj.boundMaxZ);
			projPoint = camera.projectGlobal(obj.localToGlobal(p3d));
			points.push(new Point(projPoint.x, projPoint.y));
			
			p3d = new Vector3D(obj.boundMaxX, obj.boundMaxY, obj.boundMaxZ);
			projPoint = camera.projectGlobal(obj.localToGlobal(p3d));
			points.push(new Point(projPoint.x, projPoint.y));
			
			
			
			var minX:Number = Number.MAX_VALUE;
			var minY:Number = Number.MAX_VALUE;
			var maxX:Number = Number.MIN_VALUE;
			var maxY:Number = Number.MIN_VALUE;
			
			for (var i:int = 0 ; i < points.length; i++ ) {
				minX = Math.min(minX, points[i].x);
				minY = Math.min(minY, points[i].y);
				maxX = Math.max(maxX, points[i].x);
				maxY = Math.max(maxY, points[i].y);
			}
			
			/*var gr:Graphics = _debugView.getSurface();
			gr.clear();
			gr.lineStyle(2, 0x80FF00);
			
			for (var i:int = 0 ; i < points.length; i++ ) {
				if (i == 0) {
					gr.moveTo(points[i].x, points[i].y);
					continue;
				}
				gr.lineTo(points[i].x, points[i].y);
			}*/
			
			return new Rectangle(minX, minY, maxX - minX, maxY - minY);
			//return points;
		}
		
		protected function onInvalidate(e:Event):void 
		{
			removeEventListener(Event.ENTER_FRAME, onInvalidate);
			_isInvalidation = false;
			camera.render();
			stage.focus = stage;
			dispatchEvent(new Scene3DEvent(Scene3DEvent.SCENE_UPDATED));
		}
		
		
		public function getFieldBitmapData():BitmapData {
			var rect:Rectangle = getBoundsRect(ROOM);
			
			var matr:Matrix = new Matrix();
			matr.tx = -rect.x + camera.view.x;
			matr.ty = -rect.y + camera.view.y;
			
			var bmData:BitmapData = new BitmapData(rect.width, rect.height, true, 0xff0000);
			bmData.draw(camera.view, matr, null, null, null, true);
			
			return bmData;
			
		}
		
		private function log(msg:String):void
		{
			Logger.log(this, msg);
		}
		
	}

}